package instance

import (
	"context"
	"fmt"
	"strings"
	"sync"

	"gitee.com/linxing_3/sr-core/relay"
	"gitee.com/linxing_3/sr-core/yerr"
)

var initLock = sync.Map{}

func Lock(key any) {
	lock, ok := initLock.LoadOrStore(key, &sync.Mutex{})
	if ok {
		lock.(*sync.Mutex).Lock()
	}
}

func Unlock(key any) {
	lock, ok := initLock.LoadAndDelete(key)
	if ok {
		lock.(*sync.Mutex).Unlock()
	}
}

func coverTypeKey[T any](key any) string {
	class := fmt.Sprintf("%T_%s", new(T), key)
	return strings.Trim(class, "*")
}

func AddInstance[T any](key any, value T) error {

	err := _defaultSet.AddInstance(coverTypeKey[T](key), value)
	if err != nil {
		return err
	}

	relay.MixToContext(func(ctx context.Context) context.Context {
		return context.WithValue(ctx, key, value)
	})

	return nil
}

func GetInstance[T any](key any) (*T, error) {
	key = coverTypeKey[T](key)
	obj, err := _defaultSet.GetInstance(key)
	if err != nil {
		return nil, yerr.ErrBizPluginNotFound.Errorf("plugin %T not found by key:%s", new(T), key)
	} else if obj == nil {
		return nil, yerr.ErrDataNotfound.Errorf("%T", key)
	} else if val, ok := obj.(*T); ok {
		return val, nil
	} else if val, ok := obj.(T); ok {
		return &val, nil
	} else {
		return nil, yerr.ErrBizTypeNotMatch.Errorf("plugin %T is not match to %T ", val, new(T))
	}
}

func GetOrInit[T any](key any, init func() (T, error)) (*T, error) {
	val, err := GetInstance[T](key)
	if err == nil && val != nil {
		return val, nil
	}
	// Lock(key)
	// defer Unlock(key)

	newVal, err := init()
	if err != nil {
		return nil, err
	}

	return &newVal, AddInstance(key, newVal)
}
